package org.yl.db_core.utils.db;

import org.yl.db_core.utils.TypeToCast;
import org.yl.db_core.utils.constant.SqlConstant;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

/**
 * @Description: mysql 创建实体类 方法
 * @Author: YL
 * @CreateDate: 2018/11/10 14:17
 * @Version: 1.0
 */
public class CreateMysqlBeanUtil {

    /**
     * 1.链接数据库，获取表结构数据
     * 2.反射生成实体类，方法。
     * 3.生成增删改查的方法。
     */
    private final static Logger logger = LoggerFactory.getLogger(CreateMysqlBeanUtil.class);

    private final static DBUtil dbUtil = new DBUtil();

    private final static TypeToCast typeToCast = new TypeToCast();

    private static String packageOutPath;// 指定实体生成所在包的路径

    private static String authorName;// 作者名字

    private static String tablename;// 表名

    private static String databasename;// 数据库名

    private static String projectName;// 生成实体类存放路径

    private static String dbType;// 数据库类型

    private static List<String> colnames = new ArrayList<>(); // 列名集合

    private static List<String> colTypes = new ArrayList<>(); // 列名类型集合

    private static List<String> tablenames = new ArrayList<>();// 拿到对应数据库中所有的实体类（实体类需要与其他表明做区分）

    private static boolean f_util = false; // 是否需要导入包java.util.*

    private static boolean f_sql = false; // 是否需要导入包java.sql.*

    public CreateMysqlBeanUtil(String path)
    /**获取properties的方法*/
    {
        Properties properties = new Properties();
        if (StringUtils.isEmpty(path)) {
            path = "/application.properties";
        }
        try {
            InputStream in = new PropertiesUtil(properties).getClass().getResourceAsStream(path);
            properties.load(in);
        } catch (Exception e) {
            logger.info("获取properties的方法异常，{}", e.getMessage());
        }
        /**获取配置选项*/
        this.databasename = properties.getProperty("spring.datasource.databasename").toString();
        this.tablename = properties.getProperty("spring.datasource.tablename").toString();
        this.packageOutPath = properties.getProperty("spring.datasource.packageOutPath").toString();
        this.authorName = properties.getProperty("spring.datasource.authorName").toString();
        this.projectName = properties.getProperty("spring.datasource.projectName").toString();
        this.dbType = String.valueOf(properties.getProperty("spring.datasource.hikari.jdbc-url")).split(":")[1];

    }

    public void start() {
        Connection conn = dbUtil.getConnection();
        if (databasename != null && !databasename.equals("")
                && tablename != null && !tablename.equals("")) {
            logger.info("databasename 和 tablename 不能同时存在");
        } else {
            if (databasename != null && !databasename.equals("")) {
                tablenames = new ArrayList<>();
                getAllTableEntity(conn, tablenames);
                getMysqlEntity(tablenames, conn);
            } else {
                getMysqlEntity(tablename, conn);
            }
            if (conn != null) {
                dbUtil.close(conn);
            }
            logger.info("生成完成！SUCCESS");
        }
    }

    /**
     * 创建多个实体类
     *
     * @param tablenames
     * @param conn
     */
    public void getMysqlEntity(List<String> tablenames, Connection conn) {
        if (tablenames != null) {
            for (String tablename : tablenames) {
                getMysqlEntity(tablename, conn);
            }
        }
    }

    /**
     * 获取列集合
     *
     * @param tablename
     * @param conn
     * @return
     */
    public ResultSetMetaData getDbCloumn(String tablename, Connection conn) {
        /**查询sql*/
        String sql = "SELECT * FROM " + tablename;
        PreparedStatement statement = null;
        ResultSetMetaData resultSet = null;
        try {
            statement = dbUtil.getPStatement(conn, sql);
            resultSet = statement.getMetaData();
            return resultSet;
        } catch (Exception e) {
            logger.error(e.getMessage());
        } finally {
            dbUtil.close(conn, statement);
        }
        return null;
    }

    /**
     * 创建单个个实体类
     *
     * @param tablename
     * @param conn
     */
    public void getMysqlEntity(String tablename, Connection conn) {
        /**查询sql*/
        ResultSetMetaData resultSet = null;
        PreparedStatement statement = null;
        try {
            resultSet = getDbCloumn(tablename, conn);
            /**统计表中有多少列*/
            int columnSize = resultSet.getColumnCount();
            /**列名集合*/
            colnames = new ArrayList<>();
            /**列名类型集合*/
            colTypes = new ArrayList<>();
            for (int i = 0; i < columnSize; i++) {
                colnames.add(resultSet.getColumnName(i + 1));
                colTypes.add(resultSet.getColumnTypeName(i + 1));
                if (colTypes.get(i).equalsIgnoreCase("datetime")
                        || colTypes.get(i).equalsIgnoreCase("time")
                        || colTypes.get(i).equalsIgnoreCase("timestamp")) {
                    f_util = true;
                }
                if (colTypes.get(i).equalsIgnoreCase("image")
                        || colTypes.get(i).equalsIgnoreCase("text")) {
                    f_sql = true;
                }
            }
            logger.info("列名集合:, {}", colnames);
            logger.info("列名类型集合:, {}", colTypes);
        } catch (Exception e) {
            logger.error(e.getMessage());
        } finally {
            dbUtil.close(conn, statement);
        }
        /**k开始生成实体类代码*/
        StringBuffer stringBuffer = new StringBuffer();
        {
            /**导入包名*/
            stringBuffer.append("package " + packageOutPath + ";\r\r\n");
            stringBuffer.append("import com.yl.db_core.base.BaseModel;\r\n");
            if (f_util == true) {
                stringBuffer.append("import java.util.Date;\r\n");
            }
            if (f_sql == true) {
                stringBuffer.append("import java.sql.*;\r\n");
            }
            stringBuffer.append("\r\n");
            /**注释部分*/
            stringBuffer.append("   /**\r\n");
            stringBuffer.append("     * 表名 " + tablename + " 的实体类\r\n");
            stringBuffer.append("     * " + new Date() + "\r\n");
            stringBuffer.append("     * " + authorName + "\r\n");
            stringBuffer.append("     */\r\n");
            /**实体类*/
            stringBuffer.append("\r\n\r\npublic class " + typeToCast.toClassName(tablename, "Model") + " extends BaseModel {\r\n");
            /**属性*/
            for (int i = 0; i < colnames.size(); i++) {
                stringBuffer.append("\tprivate " + coulmnTypetoJavaType(colTypes.get(i)) + " " + colnames.get(i) + ";\r\n");
            }
            stringBuffer.append("\r\n");
            /**get set 方法*/
            for (int i = 0; i < colnames.size(); i++) {
                stringBuffer.append("\tpublic void set" + typeToCast.toUpperCaseFirstOne(colnames.get(i)) + "("
                        + coulmnTypetoJavaType(colTypes.get(i)) + " " + colnames.get(i) + ") {\r\n");
                stringBuffer.append("\t\tthis." + colnames.get(i) + " = " + colnames.get(i) + ";\r\n");
                stringBuffer.append("\t}\r\n");
                stringBuffer.append("\r\n");
                stringBuffer.append("\tpublic " + coulmnTypetoJavaType(colTypes.get(i)) + " get"
                        + typeToCast.toUpperCaseFirstOne(colnames.get(i)) + "() {\r\n");
                stringBuffer.append("\t\treturn " + colnames.get(i) + ";\r\n");
                stringBuffer.append("\t}\r\n");
                stringBuffer.append("\r\n");
            }
            stringBuffer.append("\r\n");
            stringBuffer.append("\t}");
        }
        String content = stringBuffer.toString();
        logger.info("写入文件的内容:, {}", content);
        /**将内存中的内容写入到文件中*/
        {
            try {
                /**获取项目路径*/
                File directory = new File("");
                /**本地测试*/
                String outputPath = directory.getAbsolutePath() + "/"+projectName+"/src/main/java/"
                        + packageOutPath.replace(".", "/") + "/";
                /**打成jar测试*/
               /* String outputPath = directory.getAbsolutePath() + "/src/main/java/"
                        + packageOutPath.replace(".", "/") + "/";*/
                logger.info("写入文件的路径:, {}", outputPath);
                File file = new File(outputPath);
                /**判断文件是否存在 isDirectory()是检查一个对象是否是文件夹  exists()判断是否存在，可能不存在*/
                if (!file.exists() && !file.isDirectory()) {
                    /**
                     * mkdir()  创建此抽象路径名指定的目录。如果父目录不存在则创建不成功。
                     * mkdirs() 创建此抽象路径名指定的目录，包括所有必需但不存在的父目录。
                     */
                    file.mkdirs();
                }
                /**拼接实体类文件.java*/
                outputPath += typeToCast.toClassName(tablename, "Model") + ".java";
                logger.info("文件的路径:, {}", outputPath);
                File newFile = new File(outputPath);
                if (!newFile.exists()) {
                    newFile.createNewFile();
                }
                /**写入到硬盘*/
                FileWriter fw = new FileWriter(newFile);
                PrintWriter pw = new PrintWriter(fw);
                pw.println(content);
                pw.flush();
                pw.close();
            } catch (IOException e) {
                logger.error("将内存中的内容写入到文件中异常：,{}", e.getMessage());
            }
        }

    }


    /**
     * 如果 databasename 不为空时生成该库所有表
     *
     * @param conn
     * @param tablenames TABLE_CAT String => 表类别（可为 null）
     *                   TABLE_SCHEM String => 表模式（可为null）
     *                   TABLE_NAME String => 表名称
     *                   TABLE_TYPE String => 表类型
     */
    public void getAllTableEntity(Connection conn, List<String> tablenames) {
        ResultSet resultSet = null;
        try {
            DatabaseMetaData metaData = conn.getMetaData();
            resultSet = metaData.getTables(null, null, "%", null);
            while (resultSet.next()) {
                tablenames.add(resultSet.getString("TABLE_NAME"));
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        } finally {
            try {
                resultSet.close();
            } catch (SQLException e) {
                logger.error(e.getMessage());
            }
        }
    }

    /**
     * 功能：获得列的数据类型
     * @param sqlType
     * @return
     */
    private String coulmnTypetoJavaType(String sqlType) {
        if (SqlConstant.MYSQL.equalsIgnoreCase(dbType)) {
            if (sqlType.equalsIgnoreCase("bit")) {
                return "boolean";
            } else if (sqlType.equalsIgnoreCase("tinyint")) {
                return "byte";
            }else if (sqlType.equalsIgnoreCase("blob")
                    || sqlType.equalsIgnoreCase("longblob")) {
                return "byte[]";
            } /*else if (sqlType.equalsIgnoreCase("smallint")) {
                return "short";
            }*/ else if (sqlType.equalsIgnoreCase("int")
                    || sqlType.equalsIgnoreCase("smallint")
                    || sqlType.equalsIgnoreCase("smallint unsigned")
                    || sqlType.equalsIgnoreCase("int unsigned")
                    || sqlType.equalsIgnoreCase("tinyint unsigned")) {
                return "Integer";
            } else if (sqlType.equalsIgnoreCase("bigint")) {
                return "Long";
            } else if (sqlType.equalsIgnoreCase("float")) {
                return "Float";
            } else if (sqlType.equalsIgnoreCase("decimal")
                    || sqlType.equalsIgnoreCase("numeric")
                    || sqlType.equalsIgnoreCase("real")
                    || sqlType.equalsIgnoreCase("money")
                    || sqlType.equalsIgnoreCase("smallmoney")) {
                return "Double";
            } else if (sqlType.equalsIgnoreCase("varchar")
                    || sqlType.equalsIgnoreCase("char")
                    || sqlType.equalsIgnoreCase("nvarchar")
                    || sqlType.equalsIgnoreCase("nchar")
                    || sqlType.equalsIgnoreCase("text")) {
                return "String";
            } else if (sqlType.equalsIgnoreCase("datetime")
                    || sqlType.equalsIgnoreCase("timestamp")) {
                return "Date";
            } else if (sqlType.equalsIgnoreCase("image")) {
                return "Blod";
            }
        } else if (SqlConstant.POSTGRESQL.equalsIgnoreCase(dbType)){
            if (sqlType.equalsIgnoreCase("int2")
                   || sqlType.equalsIgnoreCase("int4")){
                return "Integer";
            }else if (sqlType.equalsIgnoreCase("int8")){
                return "Long";
            }else if (sqlType.equalsIgnoreCase("varchar")
                    || sqlType.equalsIgnoreCase("bpchar")
                    || sqlType.equalsIgnoreCase("text")){
                return "String";
            }else if (sqlType.equalsIgnoreCase("float8")
                    || sqlType.equalsIgnoreCase("money")){
                return "Double";
            }else if (sqlType.equalsIgnoreCase("float4")){
                return "Float";
            }else if (sqlType.equalsIgnoreCase("bit")
                    || sqlType.equalsIgnoreCase("bool")){
                return "boolean";
            }else if (sqlType.equalsIgnoreCase("numeric")){
                return "BigDecimal";
            }else if (sqlType.equalsIgnoreCase("time")
                    || sqlType.equalsIgnoreCase("timestamp")){
                return "Date";
            }
        }
        return null;
    }

    public static void main(String[] args) {
       /* CreateMysqlBeanUtil mysqlUtil = new CreateMysqlBeanUtil();
        mysqlUtil.init();*/
        /*String path = ClassUtils.getDefaultClassLoader().getResource("").getPath();
        System.out.println(path);
        String projectname = System.getProperty("user.dir");
        System.out.println(projectname);
        String pn = projectname.substring(projectname.lastIndexOf('/')+1,projectname.length());
        System.out.println(pn);*/
    }
}
